"""
Frames that map to a database table, and create the relevant mapclass objects
Written By: Daniel Ortiz
Date: December 11 2007
Version: 0.0
"""
import wx
from database.mapclass import Domain, Mailbox, Mail, AddressBook, Address, User
import handler.structures as structures

class DomainPanel(wx.Panel):
    """Domain Panel defines a port connection"""
    def __init__(self, parent, title, size = (-1, -1)):
        self.parent = parent
        wx.Panel.__init__(self, parent, style = wx.SUNKEN_BORDER, size = size)
        # Create Layout
        self.title = wx.StaticText(self, -1, title)
        self.title.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD)) 
        s = wx.StaticText(self, -1, 'Server: ') 
        self.server = wx.TextCtrl(self, -1, size = (200, -1))
        p = wx.StaticText(self, -1, 'Port: ')
        self.port = wx.TextCtrl(self, -1, size = (200, -1))
        self.ssl = wx.CheckBox(self, -1, 'SSL')

        # Sizer
        self.sizer = wx.GridBagSizer(hgap = 3, vgap = 5)
        self.sizer.Add(self.title, pos = (0, 0), span = (1, 4), flag = wx.ALIGN_CENTER)
        self.sizer.Add(s, pos = (1, 0))
        self.sizer.Add(self.server, pos = (1, 1), span = (1, 3), flag = wx.EXPAND)
        self.sizer.Add(p, pos = (2, 0))
        self.sizer.Add(self.port, pos = (2, 1), span = (1, 3), flag = wx.EXPAND)
        self.sizer.Add(self.ssl, pos = (3, 0), span = (1, 4), flag = wx.ALIGN_CENTER)

        for i in range(4):
            self.sizer.AddGrowableCol(i)
        self.SetSizer(self.sizer)
        self.Fit()

    def Validate(self):
        """Validate the form"""
        if self.server.GetValue() == '':
            return False, 'Error: Please enter a server'
        elif self.port.GetValue() == '':
            return False, 'Error: Please enter a port'
        try:
            t = int(self.port.GetValue())
            if t < 0:
                return False, 'Error: Port value is an integer greater than zero'
            else:
                return True, ''
        except Exception:
            return False, 'Error: Port value is an integer greater than zero'
        
class MailboxPanel(wx.Panel):
    """Maiblox panel defines a mailbox"""
    def __init__(self, parent, title, sibling):
        self.parent = parent
        self.sibling = sibling
        wx.Panel.__init__(self, parent, style = wx.SUNKEN_BORDER)
        # Create Members
        self.title = wx.StaticText(self, -1, title)
        self.title.SetFont(wx.Font(14, wx.SWISS, wx.NORMAL, wx.BOLD))
        e = wx.StaticText(self, -1, 'Email ')
        self.email = wx.TextCtrl(self, -1, size = (200, -1))
        self.email.Bind(wx.EVT_KILL_FOCUS, self.OnPackagedEmail)
        n = wx.StaticText(self, -1, 'Name: ')
        self.name = wx.TextCtrl(self, -1, size = (200, -1))
        p = wx.StaticText(self, -1, 'Password: ')
        self.password = wx.TextCtrl(self, -1, style = wx.TE_PASSWORD, size = (200, -1))

        # Sizer
        self.sizer = wx.GridBagSizer(hgap = 3, vgap = 5)
        self.sizer.Add(self.title, pos = (0, 0), span = (1, 4), flag = wx.ALIGN_CENTER)
        self.sizer.Add(e, pos = (1, 0))
        self.sizer.Add(self.email, pos = (1, 1), span = (1, 3), flag = wx.EXPAND)
        self.sizer.Add(n, pos = (2, 0))
        self.sizer.Add(self.name, pos = (2, 1), span = (1, 3), flag = wx.EXPAND)
        self.sizer.Add(p, pos = (3, 0))
        self.sizer.Add(self.password, pos = (3, 1), span = (1, 3), flag = wx.EXPAND)

        for i in range(4):
            self.sizer.AddGrowableCol(i)
        self.SetSizer(self.sizer)
        self.Fit()

    def Validate(self):
        """Validates if the inputs are reasonable"""
        t = self.email.GetValue().split("@")
        if len(t) != 2:
            return False, 'Error: Please enter a valid email'
        elif self.name.GetValue() == '':
            return False, 'Error: Please enter a mailbox name'
        elif self.password.GetValue() == '':
            return False, 'Error: Please enter a password'
        else:
            return True, ''

    def GetUsernameDomain(self):
        """Returns the username and domain associated with the inputted email"""
        return self.email.GetValue().split('@')

    def OnPackagedEmail(self, event):
        """Check the email and see if it is already packaged in the structures
        meta file"""
        self.parent.PrepackagedEmail(self)
    
class ListPanel(wx.ListCtrl):
    """A listctrl with a finite number of children for navigating panels"""
    def __init__(self, parent, children, width = 125):
        self.parent = parent
        wx.ListCtrl.__init__(self, parent, style = wx.LC_REPORT, size = (width, 100))
        self.parent.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected)

        if isinstance(children, dict):
            count = 0
            for key, value in children.iteritems():
                self.InsertColumn(count, key, width = width)
                count = count + 1
                for r in value:
                    self.Append([r])

    def OnItemSelected(self, event):
        self.parent.SwitchPanel(event.m_itemIndex)

    def SelectPanel(self, index):
        """From an inputted index, highlight the entity corresponding to the panel"""
        count = self.GetSelectedItemCount()
        if count >= 1:
            item = self.GetFirstSelected()
            self.Select(item, False)
            while item is not -1:
                item = self.GetNextSelected(item)
                if item != -1:
                    self.Select(item, False)
        self.Select(index)

class MailboxStatePanel(wx.Panel):
    """A panel which contains the state variables for the gauge"""
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.parent = parent
        self.range = 100
        self.num_state = 3
        self.gauge = wx.Gauge(self, -1, self.range, size = (100, -1))
        s = wx.StaticText(self, -1, 'Status: ')
        self.imap4 = wx.CheckBox(self, -1, 'IMAP4')
        self.pop3 = wx.CheckBox(self, -1, 'POP3')
        self.pop3.Bind(wx.EVT_CHECKBOX, self.OnICheck)
        self.imap4.Bind(wx.EVT_CHECKBOX, self.OnPCheck)
        self.imap4.SetValue(True)

        sizer = wx.GridBagSizer(hgap = 3, vgap = 5)
        sizer.Add(s, pos = (0, 0))
        sizer.Add(self.gauge, pos = (0, 1), span = (1, 2))
        sizer.Add(self.imap4, pos = (0, 3))
        sizer.Add(self.pop3, pos = (0, 4))
        self.SetSizer(sizer)
        self.Fit()

    def OnICheck(self, event):
        """XOR POP"""
        self.imap4.SetValue(not self.pop3.GetValue())
        if self.parent.pstate == 'PREPACKAGE':
            self.parent.SwitchPrepackage()
    def OnPCheck(self, event):
        """XOR IMAP4"""
        self.pop3.SetValue(not self.imap4.GetValue())
        if self.parent.pstate == 'PREPACKAGE':
            self.parent.SwitchPrepackage()

    def IsIMAP(self):
        """Return if the mailbox panel is imap4"""
        return self.imap4.GetValue() 

    def UpdateGauge(self, status):
        """Update the status of the gauge"""
        jump = self.range * status / self.num_state
        self.gauge.SetValue(jump)
        
class AddFrame(wx.Frame):
    """MailboxFrame allow a user to create a mailbox"""
    def __init__(self, parent, name = '' , callback = None):
        """
        parent - The parent frame
        name - The name of the frame
        callback - The function to call after the user presses add
        """
        self.parent = parent
        callback.BindVisualization(self)
        wx.Frame.__init__(self, parent, -1, name,
                          style = wx.DEFAULT_FRAME_STYLE ^(wx.RESIZE_BORDER |
                                                           wx.MAXIMIZE_BOX))
        self.CenterOnParent()
        self.Bind(wx.EVT_MOUSE_EVENTS, self.OnFocus)
        self.state = 'INPUT'
        self.pstate = 'NONE'

        # Toolbar
        toolbar = wx.GridBagSizer(hgap = 0, vgap = 0)
        self.mbutton = wx.ToggleButton(self, -1, 'Mailbox')
        self.mbutton.SetValue(True)
        self.addbutton = wx.Button(self, -1, 'Add')
        self.finishbutton = wx.Button(self, -1, 'Finish')
        self.button_count = 0
        self.button_pos = (5, 4)
        self.finishbutton.Hide()
        self.Bind(wx.EVT_BUTTON, self.OnFinish, self.finishbutton)
        self.finishbutton.Bind(wx.EVT_KILL_FOCUS, self.OnSwitchButton)
        self.Bind(wx.EVT_BUTTON, self.OnAdd, self.addbutton)
        toolbar.Add(self.mbutton, pos = (0, 0))

        # Panels
        children = {}
        self.selection = 'Mailbox'
        children['Setup'] = ['Mailbox', 'Outgoing Server', 'Incoming Server']
        self.lpanel = ListPanel(self, children)
        self.spanel = MailboxStatePanel(self)
        self.mpanel = MailboxPanel(self, 'Mailbox', self.spanel)
        msize = self.mpanel.GetSize()
        self.opanel = DomainPanel(self, 'Outgoing')
        osize = self.opanel.GetSize()

        # Calculate the minimum fitting size
        if msize < osize:
            self.panel_size = osize
        else:
            self.panel_size = msize
        self.ipanel = DomainPanel(self, 'Incoming')
        self.opanel.Hide()
        self.ipanel.Hide()
        self.panel_position = (1, 1)
        self.panel_span = (4, 4)
        self.panel_mapper = {0 : self.mpanel, 1 : self.opanel, 2 : self.ipanel}
        self.CurrentPanel = self.mpanel
        # Toolbar
        self.CreateStatusBar()
        self.BaseLineText = 'AddingMailbox'
        self.PrepackageText = 'Incoming and outgoing servers are prepackaged'
        self.SetStatusText(self.BaseLineText)

        # Sizer
        sizer = wx.GridBagSizer(hgap = 5, vgap = 5)
        sizer.Add(toolbar, pos = (0, 0), span = (1, 5), flag = wx.ALIGN_CENTER)
        sizer.Add(self.lpanel, pos = (1, 0), span = (4, 1))
        sizer.Add(self.mpanel, pos = self.panel_position, span = self.panel_span)
        sizer.Add(self.spanel, pos = (5, 0), span = (1, 4), flag = wx.ALIGN_CENTER)
        sizer.Add(self.addbutton, pos = self.button_pos)
        self.SetSizer(sizer)
        self.SwitchPanel(0)
        self.Fit()

    def SwitchPanel(self, index):
        """From the inputted index switch to the relevant panel"""
        panel = self.panel_mapper[index]
        size = panel.GetSize()
        sizer =  self.GetSizer()
        self.CurrentPanel.Hide()
        sizer.Remove(self.CurrentPanel)
        sizer.Add(panel, pos = self.panel_position, span = self.panel_span, flag = wx.EXPAND)
        sizer.SetItemMinSize(panel, self.panel_size)
        sizer.Show(panel)
        self.CurrentPanel = panel
        self.SetSizer(sizer)
        self.Layout()

    def OnAdd(self, event):
        """Attempt to add an instance of the mapclass"""
        attr = []
        for key, value in self.panel_mapper.iteritems():
            valid, err = value.Validate()
            if not valid:
                self.SetStatusText(err)
                self.lpanel.SelectPanel(key)
                return

        # Cover all cases of creation
        if self.selection == 'Mailbox':
            retr = self.CreateMailboxDomain()
            self.callback(retr, self)

    def CreateMailboxDomain(self):
        """Creates the mailbox domain pair from the panels of the AddFrame"""
        mpanel = self.panel_mapper[0]
        opanel = self.panel_mapper[1]
        ipanel = self.panel_mapper[2]
        if self.spanel.imap4.GetValue():
            protocol = 'imap4'
        else:
            protocol = 'pop3'
        username, domain = mpanel.GetUsernameDomain()
        box = Mailbox( mpanel.name.GetValue() ,0,  username, domain, mpanel.password.GetValue(),
                   self.parent.dtime, protocol, ipanel.server.GetValue(), int(ipanel.port.GetValue()),
                   ipanel.ssl.GetValue(), opanel.server.GetValue(),
                   int(opanel.port.GetValue()),  opanel.ssl.GetValue())
        return box
        

    def OnFocus(self, event):
        """Reset status text"""
        if self.state == 'INPUT':
            self.SetStatusText(self.BaseLineText)
        elif self.pstate == 'PREPACKAGE':
            self.SetStatusText(self.PrepackageText)

    def UpdateCheckStatus(self, status, status_text, error = ''):
        """Update the user to the status of the check"""
        if not status:
            self.state = 'INPUT'
            self.SetStatusText(status_text + ':' + error)
            self.spanel.UpdateGauge(0)
        else:
            self.state = 'VALIDATE'
            self.SetStatusText(status_text)
            self.spanel.UpdateGauge(status)
            if status == 3:
                self.SwitchButton()

    def SwitchPrepackage(self):
        """Switch between prepackaged forms of emails"""
        self.PrepackagedEmail(self.panel_mapper[0])

    def PrepackagedEmail(self, attr):
        """Handle the possibility of a prepackaged email"""
        try:
            username, domain = attr.GetUsernameDomain()
        except ValueError:
            return

        if self.spanel.IsIMAP():
            state = 'imap4'
        else:
            state = 'pop3'
        try:
            d = structures.mail_strip(domain)
            domain = getattr(structures, '%s_%s' % (state, d))
        except AttributeError:
            domain = None
        self.UpdateSiblings(domain)

    def UpdateSiblings(self, domain):
        """Update the siblings panel with metaclass information"""
        if domain:
            self.pstate = 'PREPACKAGE'
            self.state = 'PREPACKAGE'
            self.SetStatusText(self.PrepackageText)
            opanel = self.panel_mapper[1]
            opanel.server.SetValue(domain['oserver'])
            opanel.port.SetValue(str(domain['oport']))
            opanel.ssl.SetValue(domain['ossl'])
            ipanel = self.panel_mapper[2]
            ipanel.server.SetValue(domain['iserver'])
            ipanel.port.SetValue(str(domain['iport']))
            ipanel.ssl.SetValue(domain['issl'])
        elif self.pstate == 'PREPACKAGE':
            self.pstate = 'UNPACKAGED'
            self.state = 'INPUT'
            self.SetStatusText(self.BaseLineText)
            opanel = self.panel_mapper[1]
            opanel.server.SetValue('')
            opanel.port.SetValue('')
            opanel.ssl.SetValue(False)
            ipanel = self.panel_mapper[2]
            ipanel.server.SetValue('')
            ipanel.port.SetValue('')
            ipanel.ssl.SetValue(False)

    def OnFinish(self, event):
        """Finish the add frame. Closes the frame"""
        self.Close()

    def OnSwitchButton(self, event):
        """Switch the button back to the steady state"""
        self.SwitchButton()
        
    def SwitchButton(self):
        """Switch between the add and finish button"""
        sizer = self.GetSizer()
        if self.button_count == 0:
            sizer.Remove(self.addbutton)
            self.button_count = self.button_count + 1
            self.addbutton.Hide()
            self.finishbutton.Show()
            sizer.Add(self.finishbutton, self.button_pos)
            self.finishbutton.SetFocus()
            self.SetSizer(sizer)
            self.Layout()
        else:
            sizer.Remove(self.finish_button)
            self.button_count = self.button_count - 1
            self.addbutton.Show()
            self.finishbutton.Hide()
            sizer.Add(self.addbutton, self.button_pos)
            self.SetSizer(sizer)
            self.Layout()

        
if __name__ == '__main__':
    app = wx.PySimpleApp()
    frame = DeleteFrame(None, 'Test')
    frame.Show()
    app.MainLoop()
